﻿using Bit.Art.Core.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Linq;
using Newtonsoft.Json;
using Microsoft.Extensions.Configuration;

namespace Bit.Art.Core.Extensions
{
    public static class Utility
    {
        /// <summary>
        /// 将一个对象序列化成二进制数据
        /// </summary>
        /// <typeparam name="T">对象类型，需要实现<seealso cref="IFixByteSerializable"/>接口</typeparam>
        /// <param name="array">要序列化的对象</param>
        /// <returns></returns>
        public static byte[] ToFixedBytes(IEnumerable<IFixByteSerializable> array)
        {
            if (array == null) return null;
            var count = array.Count();
            if (count == 0) return null;

            var byteSize = array.First().ByteSize;
            var len = count * byteSize;
            var bytes = new byte[len];
            var i = 0;
            foreach (var item in array)
            {
                var serialized = item.ToFixedBytes();
                Array.Copy(serialized, 0, bytes, i * byteSize, byteSize);
                i++;
            }

            return bytes;
        }

        public static T[] ArrayFromBytes<T>(byte[] bytes) where T : IFixByteSerializable, new()
        {
            var byteSize = new T().ByteSize;
            if (bytes == null || bytes.Length < byteSize) return null;
            var len = bytes.Length / byteSize;
            var abs = new T[len];
            for (var i = 0; i < len; ++i)
            {
                var serialized = new byte[byteSize];
                Array.Copy(bytes, i * byteSize, serialized, 0, serialized.Length);
                abs[i] = new T();
                abs[i].InitFrom(serialized);
            }

            return abs;
        }

        public static void Serialize(decimal value, byte[] destination, int destinationIdx)
        {
            var array = decimal.GetBits(value);

            for (var i = 0; i < array.Length; ++i)
            {
                var bytes = BitConverter.GetBytes(array[i]);
                Array.Copy(bytes, 0, destination, destinationIdx + i * 4, 4);
            }
        }

        public static decimal DecimalFromBytes(byte[] bytes, int startIdx)
        {
            var ints = new int[4];
            for (var i = 0; i < 4; ++i)
            {
                var array = new byte[4];
                Array.Copy(bytes, startIdx + i * 4, array, 0, 4);
                // Console.WriteLine($"decimal from bytes: {BitConverter.ToString(array)}");
                ints[i] = BitConverter.ToInt32(array, 0);
            }

            // Console.WriteLine($"decimal from : {ints[0]} - {ints[1]} - {ints[2]} - {ints[3]}");
            return new decimal(ints);
        }

        public static bool SplitMarket(string market, out string quote, out string coin)
        {
            if (string.IsNullOrWhiteSpace(market)) throw new ArgumentNullException(nameof(market));

            quote = null;
            coin = null;
            var idx = market.IndexOf('/');
            if (idx < 0)
            {
                return false;
            }
            else
            {
                coin = market.Substring(0, idx);
                quote = market.Substring(idx + 1);
                return true;
            }
        }

        /// <summary>
        /// Gets a timestamp in milliseconds.
        /// </summary>
        /// <returns>Timestamp in milliseconds.</returns>
        public static long GetUnixTimestamp(DateTime? time = null)
        {
            var dtOffset = new DateTimeOffset(time ?? DateTime.Now);
            return dtOffset.ToUnixTimeMilliseconds();
        }

        public static double GetUnixTimestampDoubleFormat(DateTime? time = null)
        {
            if (!time.HasValue)
                time = DateTime.UtcNow;
            else
                time = time.Value.ToUniversalTime();

            return time.Value.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
        }

        public static DateTime FromUnixTimestamp(long value)
        {
            // 参考文档：https://stackoverflow.com/a/26225951
            return DateTimeOffset.FromUnixTimeMilliseconds(value).UtcDateTime;
        }

        public static DateTime FromUnixTimestamp(double value)
        {
            var date = new DateTime(1970, 1, 1);
            var utc = date.AddSeconds(value);
            //var test = utc.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
            return utc.ToLocalTime();
        }

        /// <summary>
        /// 将一个对象序列化成json格式
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="obj">对象实例</param>
        /// <returns>json文本</returns>
        public static string ToJson<T>(this T obj)
        {
            return JsonConvert.SerializeObject(obj);
        }

        /// <summary>
        /// 将一个json文本反序列化成一个对象
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="json">json文本</param>
        /// <returns>序列化后的对象</returns>
        public static T From<T>(string json)
        {
            if (!string.IsNullOrEmpty(json))
            {
                var item = JsonConvert.DeserializeObject<T>(json);
                return item;
            }
            else
            {
                return default(T);
            }
        }

        /// <summary>
        /// 配置文件根节点
        /// </summary>
        public static IConfigurationRoot Configuration { get; set; }
    }
}
